#pragma once
#include <i_component_def.h>
#include <i_component.h>
#include <i_component_container.h>

struct IComponentPackage;

class CComponentFactory
{
public:
	CComponentFactory(IComponentPackage& package, const std::string& name) : m_package(&package), m_component_name(name)
	{}

	virtual ~CComponentFactory(){}

	const std::string& GetComponentName() const {
		return m_component_name;
	}
	IComponentPackage* GetComponentPackage() const {
		return m_package;
	}

	virtual CComponentPtr CreateInstance() = 0;

	void AddInstance(CComponent& com) {
		m_all_instances.insert(&com);
	}

	void DelInstance(CComponent& com) {
		m_all_instances.erase(&com);
	}

	void RemoveAllInstance() {
		std::vector<CComponent*> com_vec;
		com_vec.reserve(m_all_instances.size());
		for (CComponent* com : m_all_instances)
			com_vec.push_back(com);

		for (CComponent* com : com_vec)
			com->GetContainer()->RemoveComponent(com->getComID());
	}
protected:
	std::string m_component_name;
	std::set<CComponent*> m_all_instances;
	IComponentPackage* const m_package;
};

typedef std::shared_ptr<CComponentFactory> CComponentFactoryPtr;

template<class T>
class TComponentFactory : public CComponentFactory
{
public:
	TComponentFactory(IComponentPackage& package, const char* name = typeid(T::interface_t).name()) : CComponentFactory(package, name){
		static_assert(std::is_base_of<CComponent, T::implement_t>::value, "must derived from CComponent");
	}

	virtual CComponentPtr CreateInstance() {
		auto com_ptr = std::make_shared<T::implement_t>();
		com_ptr->SetFactory(*this);
		return com_ptr;
	}
};

struct IComponentPackage
{
	template<class... T>
	std::vector<bool> Export() {
		return { Export(std::make_shared<TComponentFactory<T>>(*this))... };
	}

	virtual bool Export(const CComponentFactoryPtr& factory_ptr) = 0;
};


struct IComponentFactoryCenter
{
	virtual bool LoadPackage(const std::string& filepath) = 0;

	virtual int LoadDir(const std::string& dir_path) = 0;

	virtual bool UnloadPackage(const std::string& filename) = 0;

	virtual IComponentContainerPtr CreateContainer() = 0;
};

typedef std::unique_ptr<IComponentFactoryCenter> IComponentFactoryCenterPtr;

COMPONENT_DLL_EXPORT IComponentFactoryCenterPtr CreateFactoryCenter();